//--------------------------------------------------------------------------------
// This file is part of the Windows Workflow Foundation Sample Code
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// This source code is intended only as a supplement to Microsoft
// Development Tools and/or on-line documentation.  See these other
// materials for detailed information regarding Microsoft code samples.
// 
// THIS CODE AND INFORMATION ARE PROVIDED AS IS WITHOUT WARRANTY OF ANY
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//--------------------------------------------------------------------------------

using System;
using System.Drawing;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Workflow.ComponentModel;
using System.Workflow.ComponentModel.Compiler;
using System.Workflow.Runtime;
using System.Collections.ObjectModel;
using System.Collections.Generic;
using Microsoft.Samples.Workflow.UI.Design;

namespace Microsoft.Samples.Workflow.UI
{
    [Designer(typeof(InteractionActivityDesigner))]
    [ActivityValidator(typeof(InteractionActivityValidator))]
    [ToolboxBitmap(typeof(InteractionActivity), "Resources.InteractionActivity.png")]
    public class InteractionActivity : InteractionBaseActivity, IActivityEventListener<ActivityExecutionStatusChangedEventArgs>
    {
        static readonly DependencyProperty ActiveChildIndexProperty = DependencyProperty.Register("ActiveChildIndex", typeof(int), typeof(InteractionActivity));
        static readonly DependencyProperty ActiveChildQualifiedNameProperty = DependencyProperty.Register("ActiveChildQualifiedName", typeof(string), typeof(InteractionActivity));

        [NonSerialized]
        bool activeChildRemovedInDynamicUpdate = false;

        public InteractionActivity()
        {
        }

        public InteractionActivity(string name)
            : base(name)
        {
        }

        protected override ActivityExecutionStatus OnExecute(ActivityExecutionContext executionContext)
        {
            this.SetValue(ActiveChildIndexProperty, -1);
            if (!ExecuteNextChild(executionContext))
            {
                return ActivityExecutionStatus.Closed;
            }
            else
            {
                return ActivityExecutionStatus.Executing;
            }
        }

        protected override ActivityExecutionStatus OnCancel(ActivityExecutionContext executionContext)
        {
            for (int i = (this.EnabledActivities.Count - 1); i >= 0; i--)
            {
                Activity childActivity = this.EnabledActivities[i];

                if (childActivity.ExecutionStatus == ActivityExecutionStatus.Executing)
                {
                    executionContext.CancelActivity(childActivity);
                    return ActivityExecutionStatus.Canceling;
                }

                if (childActivity.ExecutionStatus == ActivityExecutionStatus.Canceling || childActivity.ExecutionStatus == ActivityExecutionStatus.Faulting)
                {
                    return ActivityExecutionStatus.Canceling;
                }

                if (childActivity.ExecutionStatus == ActivityExecutionStatus.Closed)
                {
                    return ActivityExecutionStatus.Closed;
                }
            }

            return ActivityExecutionStatus.Closed;
        }

        protected override void OnClose(IServiceProvider provider)
        {
            this.RemoveProperty(ActiveChildIndexProperty);
            this.RemoveProperty(ActiveChildQualifiedNameProperty);
        }

        protected override void OnActivityChangeAdd(ActivityExecutionContext executionContext, Activity addedActivity)
        {
            int addedIndex = this.EnabledActivities.IndexOf(addedActivity);
            int currentIndex = (int)this.GetValue(ActiveChildIndexProperty);
            if (addedIndex <= currentIndex)
            {
                this.SetValue(ActiveChildIndexProperty, ++currentIndex);
            }
             
            base.OnActivityChangeAdd(executionContext, addedActivity);
        }

        protected override void OnActivityChangeRemove(ActivityExecutionContext executionContext, Activity removedActivity)
        {
            string activeActivityName = (string)this.GetValue(ActiveChildQualifiedNameProperty);
            IList<Activity> childActivities = this.EnabledActivities;
            if (activeActivityName.Equals(removedActivity.QualifiedName))
            {
                if (childActivities.Count == 0)
                {
                    this.SetValue(ActiveChildIndexProperty, -1);
                }
                else
                {
                    for (int i = (childActivities.Count - 1); i >= 0; i--)
                    {
                        if (childActivities[i].ExecutionStatus == ActivityExecutionStatus.Closed)
                        {
                            this.SetValue(ActiveChildIndexProperty, i);
                            break;
                        }
                    }
                }
                activeChildRemovedInDynamicUpdate = true;
            }
                
            base.OnActivityChangeRemove(executionContext, removedActivity);
        }

        protected override void OnWorkflowChangesCompleted(ActivityExecutionContext executionContext)
        {
            if (activeChildRemovedInDynamicUpdate)
            {
                //The active child is removed, we need to execute the next child.  
                if (this.ExecutionStatus == ActivityExecutionStatus.Canceling ||
                   (this.ExecutionStatus == ActivityExecutionStatus.Faulting))
                {
                    executionContext.CloseActivity();
                }
                else if (this.ExecutionStatus == ActivityExecutionStatus.Executing)
                {
                    if (!ExecuteNextChild(executionContext))
                    {
                        executionContext.CloseActivity();
                    }
                }
            }
            activeChildRemovedInDynamicUpdate = false;

            base.OnWorkflowChangesCompleted(executionContext);
        }

        void IActivityEventListener<ActivityExecutionStatusChangedEventArgs>.OnEvent(Object sender, ActivityExecutionStatusChangedEventArgs e)
        {
            if (sender == null)
            {
                throw new ArgumentNullException("sender");
            }
            if (e == null)
            {
                throw new ArgumentNullException("e");
            }

            ActivityExecutionContext context = sender as ActivityExecutionContext;
            if (context == null)
            {
                throw new ArgumentException(SR.GetString(SR.Error_InvalidParameterType, "sender", typeof(ActivityExecutionContext).FullName));
            }

            e.Activity.UnregisterForStatusChange(Activity.ClosedEvent, this);
            InteractionActivity sendAndWaitActivity = context.Activity as InteractionActivity;
            if (sendAndWaitActivity == null)
            {
                throw new ArgumentException(SR.GetString(SR.Error_InvalidParameterType, "context.Activity", typeof(InteractionActivity).FullName));
            }

            if (sendAndWaitActivity.ExecutionStatus == ActivityExecutionStatus.Canceling)
            {
                context.CloseActivity();
            }
            else if (sendAndWaitActivity.ExecutionStatus == ActivityExecutionStatus.Executing)
            {
                if (!ExecuteNextChild(context))
                {
                    context.CloseActivity();
                }
            }
        }

        bool ExecuteNextChild(ActivityExecutionContext executionContext)
        {
            int currentIndex = (int)this.GetValue(ActiveChildIndexProperty);
            IList<Activity> seqList = this.EnabledActivities;
            if (currentIndex == (seqList.Count - 1))
            {
                return false;
            }

            Activity currentChild = seqList[++currentIndex];
            currentChild.RegisterForStatusChange(Activity.ClosedEvent, this);
            executionContext.ExecuteActivity(currentChild);
            this.SetValue(ActiveChildIndexProperty, currentIndex);
            this.SetValue(ActiveChildQualifiedNameProperty, currentChild.QualifiedName);
            return true;
        }
    }
}
